package com.jxtc.enterprise.employee.controller;

import com.alibaba.fastjson.JSON;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.jxtc.enterprise.common.entity.*;
import com.jxtc.enterprise.common.utils.Result;
import com.jxtc.enterprise.common.utils.ResultCode;
import com.jxtc.enterprise.common.utils.ResultGenerator;
import com.jxtc.enterprise.common.vo.OrderLockerBoxNoInfoForCreateOrderVo;
import com.jxtc.enterprise.employee.dto.CreateOrderAgainDTO;
import com.jxtc.enterprise.employee.dto.OrderCreateDTO;
import com.jxtc.enterprise.employee.serviceImpl.JxOrderServiceImpl;
import com.jxtc.enterprise.employee.vo.ShoppingCartVO;
import com.jxtc.enterprise.employee.vo.WxPayVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/employee/orderPay")
@RequiredArgsConstructor
@Slf4j
public class OrderPaymentController {

    private final WxPayService payService;

    private final JxOrderServiceImpl jxOrderServiceImpl;

    /**
     * 购物车页面，点击【立即下单】 跳转到 订单确认页面，然后点击 【提交订单】，调用此接口，传递如下信息
     * （1）用户地址，包括站点id，取餐地址（大厦+楼层+门牌号）
     * （2）用户挑选的菜品列表，包括数量，规格
     * （3）使用的优惠券
     * （4）总订单金额
     * （5）备注
     * {
     * "siteId":"1693633601729568769",
     * "userId": 11111,
     * "nameTakingMeal": "皮皮喵",
     * "phoneTakingMeal": "15011111111",
     * "address":"xx科技园-xx大厦xx栋-xx层-xx取餐点",
     * "shoppingCartIds":["shoppingcartId1", shoppingcartId2", shoppingcartId3"]
     * "couponIds":"xxxxx",
     * "couponValue": 150, // 优惠券的优惠金额
     * "totalPrice": 1600, // 最终支付的费用
     * "note": "不吃香菜"
     * }
     */
    @ApiResponse(responseCode = "200", description = "若响应体中 code 为 200 表示查询公司信息成功，data 字段为调起微信支付所需的相关信息")
    @ApiResponse(responseCode = "200", description = "若响应体中 code 为 400 表示下单失败，message 字段则会提供错误信息")
    @Operation(summary = "2.6 下单", tags = {"2. 用户小程序-下单"})
    @PostMapping(value = "/createOrder")
    public Result<?> createOrder(@Valid @RequestBody OrderCreateDTO dto) {
        log.info("创建订单，请求参数: {}", dto);
        List<ShoppingCart> shoppingCarts = jxOrderServiceImpl.listShoppingCartByIds(dto.getShoppingCartIds());

        // 1. 对前端传递过来的价格进行核对。对每个菜品的价格进行累加，比较前端传递过来的金额是否与累加金额相等
        jxOrderServiceImpl.checkOrderPrice(shoppingCarts, dto);

        // 2. 对用户选购的菜品是否有库存进行检查
        List<ShoppingCartVO> outStockFoodList = jxOrderServiceImpl.checkFoodStock(dto.getMenuId(), shoppingCarts);
        outStockFoodList = new ArrayList<>();
        // 当某些商品无库存时，返回错误码以及没有库存的菜品id列表，小程序端需要弹窗提示 某些菜品无库存，需要提示用户 重新购买其他商品
        if (!outStockFoodList.isEmpty()) {
            Result<Object> result = new Result<>();
            // 将菜品名称用 "/" 拼接成提示信息
            String msg = outStockFoodList.stream()
                    .map(ShoppingCartVO::getFoodName)
                    .collect(Collectors.joining("、"));
            result.setCode(ResultCode.FAIL);
            result.setMessage("【" + msg + "】没有库存，请选择其他菜品");
            // 将没有库存的菜品信息返回给前端
            result.setData(outStockFoodList);
            return result;
        }

        // 3. 创建订单信息，并判断是否调用微信支付接口
        try {
            OrderLockerBoxNoInfoForCreateOrderVo lockerBoxNoInfoForCreateOrderVo = new OrderLockerBoxNoInfoForCreateOrderVo();
            WxPayVo wxPayVo = jxOrderServiceImpl.createOrder(shoppingCarts, dto, lockerBoxNoInfoForCreateOrderVo);
            // 当不需要微信支付，0元下单时，立即分配编号
            if (StringUtils.hasText(lockerBoxNoInfoForCreateOrderVo.getOrderId())) {
                // 无需微信字符下单成功后，为用户的餐品分配 货道编号，并将货道信息更新到 order_detail 表中
                jxOrderServiceImpl.assignLockerBoxNoForOrder(lockerBoxNoInfoForCreateOrderVo.getOrderId(), lockerBoxNoInfoForCreateOrderVo.getCompanyId(),
                        lockerBoxNoInfoForCreateOrderVo.getDateFlag(), lockerBoxNoInfoForCreateOrderVo.getMealTypeEn());
            }
            return ResultGenerator.genSuccessResult(wxPayVo);
        } catch (WxPayException e) {
            log.error("微信支付异常", e);
            return new Result<>(ResultCode.INTERNAL_SERVER_ERROR.code(), "微信支付异常", e.getMessage());
        }
    }

    /**
     * 由微信支付平台的服务调用 此接口，将微信支付结果推送给 点餐系统后端服务；
     *
     * @param request
     * @return
     * @throws WxPayException
     */
    @Operation(summary = "4.1 微信支付成功后回调接口（与前端无关，前端不需要调用）", tags = {"4. 微信支付回调接口"})
    @PostMapping("/notify")
    public Result<?> notifyHandle(HttpServletRequest request) throws WxPayException {
        String requestBody = getRequestBody(request);
        log.debug("回调参数：{}", requestBody);
        SignatureHeader signatureHeader = new SignatureHeader();
        signatureHeader.setNonce(request.getHeader("Wechatpay-Nonce"));
        signatureHeader.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
        signatureHeader.setSignature(request.getHeader("Wechatpay-Signature"));
        signatureHeader.setSerial(request.getHeader("Wechatpay-Serial"));
        log.debug("回调 header：{}", JSON.toJSONString(signatureHeader));
        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = payService.parseOrderNotifyV3Result(requestBody, signatureHeader);
        log.debug("回调 wxPayOrderNotifyV3Result：{}", JSON.toJSONString(wxPayOrderNotifyV3Result.getResult()));
        // 根据支付回调结果，若支付成功，则将订单状态从 toPay 修改为 toSend
        jxOrderServiceImpl.handleOrderNotify(wxPayOrderNotifyV3Result);
        return Result.ok();
    }

    /**
     * 查询订单的支付状态
     *
     * @param orderId 订单 ID
     * @return
     */
    @ApiResponse(responseCode = "200", description = "若响应体中 code 为 200 表示查询订单的支付状态成功，data 字段为公司信息")
    @Parameter(name = "orderId", description = "订单 ID", example = "c752804d0cd410267f404dd3f0457a57")
    @Operation(summary = "4.2 查询微信支付订单支付状态", tags = {"4. 微信支付回调接口"})
    @RequestMapping(value = "/queryOrderPayResult", method = RequestMethod.GET)
    public Result<WxPayOrderQueryV3Result> queryOrderPayResult(@RequestParam("orderId") String orderId) {
        WxPayOrderQueryV3Result payOrderQueryV3Result = jxOrderServiceImpl.queryOrderPayResult(orderId);
        return ResultGenerator.genSuccessResult(payOrderQueryV3Result);
    }

    /**
     * 接口2.4
     * 此接口从 redis中获取之前下单时，调用 "发起支付接口" 获取的 预支付信息，继续复用之前的交易信息，当前调用 “发起支付接口”时，设置的 预支付信息有效期为 1天，因此在一天之内，可以复用，不需要再次调用接口，获取新的 预支付信息；
     * 若redis中没有，则重新调用微信支付的 发起支付接口，重新获取一次 预支付信息
     * 已经下单，但是终止了支付，此接口用来继续之前未完成的支付过程
     */
    @RequestMapping(value = "/toPay", method = RequestMethod.GET)
    public Result<?> toPay(@RequestParam("orderId") String orderId) {
        try {
            WxPayVo wxPayVo = jxOrderServiceImpl.toPay(orderId);
            return ResultGenerator.genSuccessResult(wxPayVo);
        } catch (WxPayException e) {
            log.error("微信支付异常", e);
            return new Result<>(ResultCode.INTERNAL_SERVER_ERROR.code(), "微信支付异常", e.getMessage());
        }
    }

    /**
     * 读取请求数据流
     *
     * @param request
     * @return
     */
    private String getRequestBody(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder();

        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;

            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }

        } catch (IOException e) {
            log.error("读取数据流异常: ", e);
        }

        return sb.toString();
    }

    /**
     * 取消订单，取消订单后，用户支付的费用会原路返回；取消后的订单，用户可以再次下单
     *
     * @return
     */
    @ApiResponse(responseCode = "200", description = "若响应体中 code 为 200 表示取消订单成功")
    @ApiResponse(responseCode = "200", description = "若响应体中 code 为 500 表示取消订单异常，message 字段则会提供错误信息")
    @Parameter(name = "orderId", description = "订单 ID", example = "c752804d0cd410267f404dd3f0457a57")
    @Operation(summary = "2.7 取消订单", tags = {"2. 用户小程序-下单"})
    @GetMapping("/cancelOrder")
    public Result<String> cancelOrder(@RequestParam("orderId") String orderId) {
        try {
            jxOrderServiceImpl.cancelOrder(orderId);
            return ResultGenerator.genSuccessResult("success", "订单取消成功，可以在全部订单中查看已取消的订单，并再次下单");
        } catch (WxPayException e) {
            log.error("取消订单异常", e);
            return new Result<>(ResultCode.INTERNAL_SERVER_ERROR.code(), "微信支付退款异常", e.getMessage());
        }
    }


    /**
     * 取消订单并退款完成后，微信支付平台，退款成功后，推送消息给我们的业务服务
     *
     * @return
     */
    @Operation(summary = "4.3 微信退款成功后回调接口（与前端无关，前端不需要调用）", tags = {"4. 微信支付回调接口"})
    @PostMapping("/refundCallback")
    public Result<?> refundCallback(HttpServletRequest request) throws WxPayException {

        String requestBody = getRequestBody(request);
        log.debug("回调参数：{}", requestBody);
        SignatureHeader signatureHeader = new SignatureHeader();
        signatureHeader.setNonce(request.getHeader("Wechatpay-Nonce"));
        signatureHeader.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
        signatureHeader.setSignature(request.getHeader("Wechatpay-Signature"));
        signatureHeader.setSerial(request.getHeader("Wechatpay-Serial"));
        log.debug("回调 header：{}", JSON.toJSONString(signatureHeader));
        WxPayRefundNotifyV3Result wxPayRefundNotifyV3Result = payService.parseRefundNotifyV3Result(requestBody, signatureHeader);
        log.debug("回调 wxPayOrderNotifyV3Result：{}", JSON.toJSONString(wxPayRefundNotifyV3Result.getResult()));
        // handleRefundSuccessResult(wxPayRefundNotifyV3Result.getResult().getOutTradeNo());
        jxOrderServiceImpl.handleRefundSuccessResult(wxPayRefundNotifyV3Result.getResult().getOutTradeNo());

        return Result.ok();
    }

    /**
     * 接口 3.17 不建议显示 【取消支付】按钮，此接口已实现，暂时不对小程序端开发，暂时不使用
     * 使用场景
     * 当用户下单后，小程序端唤起支付后，用户没有支付，然后用户退出 创建订单页面；此时订单处于 未支付状态，此订单上可以显示显示【取消支付】 按钮
     *
     * @param orderId
     * @return
     */
    public Result<?> abortPay(@RequestParam("orderId") String orderId) {
        return jxOrderServiceImpl.abortPay(orderId);
    }

    /**
     * TODO 待实现
     */

    @ApiResponse(responseCode = "200", description = "若响应体中 code 为 200 表示再来一单时获取历史购物车信息成功，data 字段为购物车信息")
    @ApiResponse(responseCode = "200", description = "若响应体中 code 为 400 表示再来一单信息失败，message 字段则会提供错误信息")
    @Operation(summary = "2.8 再来一单", tags = {"2. 用户小程序-下单"})
    @PostMapping(value = "/createOrderAgain")
    public Result<List<ShoppingCartVO>> createOrderAgain(@Valid @RequestBody CreateOrderAgainDTO dto) {
        log.info("/createOrderAgain, orderId: {}, phone: {}", dto.getOrderId(), dto.getPhone());

        // 查询历史购物车数据，并通过 phone + menuId 清空当前购物车数据
        List<ShoppingCart> shoppingCarts = jxOrderServiceImpl.listAndClearShoppingCartByOrderId(dto.getOrderId(), dto.getPhone());

        // 基于当前时间判断是否可以下单，不能下单时给前端错误提示信息
        jxOrderServiceImpl.checkIfCouldCreateOrderAgain(shoppingCarts, dto.getPhone());

        // 将购物车数据再次添加到数据库中
        List<ShoppingCart> newShoppingCartList = jxOrderServiceImpl.addShoppingCartAgain(shoppingCarts);

        List<ShoppingCartVO> voList = newShoppingCartList.stream()
                .map(ShoppingCartVO::new)
                .collect(Collectors.toList());
        return ResultGenerator.genSuccessResult(voList);
    }
}
